#include "jsEngine.h"

#include <v8.h>
#include <stdlib.h>
#include "geocoder.h"

using namespace std;

JsEngine::JsEngine() {	
	
}

JsEngine::~JsEngine() {	
	v8::V8::Dispose(); 
}

const char* ToCString(const v8::String::Utf8Value& value);
void ReportException(v8::TryCatch* handler);

v8::Handle<v8::Value> Print(const v8::Arguments& args);
v8::Handle<v8::Value> GoogleMapsRequest(const v8::Arguments& args);
Geocoder * geocoder;

void JsEngine::setGeocoder(Geocoder* newGeocoder){
	geocoder = newGeocoder;
}

void JsEngine::runJS(string code, string input, vector<Place>* places)
{
  // Create a stack-allocated handle scope.
  v8::HandleScope handle_scope;
  
  // Create a new execution environment containing the built-in
  // functions  
  v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();

  // Bind the global 'print' function to the C++ Print callback.
  global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print));
  global->Set(v8::String::New("GoogleMapsRequest"),v8::FunctionTemplate::New(GoogleMapsRequest));

  v8::Handle<v8::Context> context = v8::Context::New(NULL, global);
  // Enter the newly created execution environment.
  v8::Context::Scope context_scope(context);

  /*// Create a stack-allocated handle scope.
  v8::HandleScope handle_scope;
  // Create a new context.
  v8::Persistent<v8::Context> context = v8::Context::New();  
  // Enter the created context for compiling and
  // running the hello world script. 
  v8::Context::Scope context_scope(context);*/
  
  // Create a string containing the JavaScript source code.
  v8::Handle<v8::String> source = v8::String::New(code.c_str());//String::New("'Hello' + ', World!'");


  v8::Handle<v8::Script> script;
  {
    // Compile script in try/catch context.
    v8::TryCatch try_catch;	
    script = v8::Script::Compile(source);
    if (script.IsEmpty()) {
      // Print errors that happened during compilation.
		ReportException(&try_catch);
    }
  }

  {
    v8::TryCatch try_catch;
    script->Run();
    if (try_catch.HasCaught()) {
      //error
		ReportException(&try_catch);
    }
  }
     
  v8::Handle<v8::String> fun_name = v8::String::New("ProcessLjComment");
  v8::Handle<v8::Value> process_val = v8::Context::GetCurrent()->Global()->Get(fun_name);

  // If there is no Process function, or if it is not a function, bail out 
  if (!process_val->IsFunction()) {
		//error
    return;
  }

  // It is a function; cast it to a Function
  v8::Handle<v8::Function> process_fun = v8::Handle<v8::Function>::Cast(process_val);
 
  v8::Handle<v8::String> input_line = v8::String::New(input.c_str());
  if (input_line == v8::Undefined()) {
		//error
    return;
  }

    const int argc = 1;
    v8::Handle<v8::Value> argv[argc] = { input_line };

    v8::Handle<v8::Value> jsresult;
    {
      v8::TryCatch try_catch;
      jsresult = process_fun->Call(v8::Context::GetCurrent()->Global(), argc, argv);
      if (try_catch.HasCaught()) {		   
		ReportException(&try_catch);
    return;
      }
    }

//////////////////////////////////////////////////////////////////////
//casting result to array
	if (!jsresult->IsArray()) {
		//error. not an array  	 
		return;
    }
	// It is an array; cast it to a Array
	v8::Handle<v8::Array> jsResultPlaces = v8::Handle<v8::Array>::Cast(jsresult);

	for (unsigned i = 0; i != jsResultPlaces->Length(); ++i)
	{		
		//casting elements into objects	
		v8::Local<v8::Object> jsPlace = 
			v8::Local<v8::Object>::Cast(jsResultPlaces->Get(v8::Integer::New(i)));		


		string props[4]; //c++ string place properties
		// получаем список имен свойств
		v8::Local<v8::Array> jsPlaceProps = jsPlace->GetPropertyNames(); 
		for (unsigned j = 0; j < jsPlaceProps->Length(); j++)
		{			
			// получаем имя
		    v8::Local<v8::String> key = 
				v8::Local<v8::String>::Cast(jsPlaceProps->Get(v8::Integer::New(j)));

			// получаем значение
		    v8::Local<v8::Value> jsVal = jsPlace->Get(key);
			v8::String::Utf8Value val(jsVal);
			std::string property(ToCString(val));
			props[j] = property;
			//cout<<"result: "<<result<<"\n";
		}

		//generation place
		Place place(props[0],props[1]); //name, address
		place.setCoordinates(strtod(props[2].c_str(), NULL),  //lat, lon
							 strtod(props[3].c_str(), NULL)
							);

		cout<<place.getName()<<" "<<place.getAddress()<<" "<<place.getLat()<<" "<<place.getLon()<<"\n";
		places->push_back(place);

	}

//////////////////////////////////////////////////////////////////////

//debug code:
// v8::String::Utf8Value str(jsresult);
// const char* cstr = ToCString(str);
// std::string result(ToCString(str)); 
  	return;

  // Dispose the persistent context.
  //context.Dispose();
}


void ReportException(v8::TryCatch* try_catch) {
  v8::HandleScope handle_scope;
  v8::String::Utf8Value exception(try_catch->Exception());
  const char* exception_string = ToCString(exception);
  v8::Handle<v8::Message> message = try_catch->Message();
  if (message.IsEmpty()) {
    // V8 didn't provide any extra information about this error; just
    // print the exception.
    printf("%s\n", exception_string);
  } else {
    // Print (filename):(line number): (message).
    v8::String::Utf8Value filename(message->GetScriptResourceName());
    const char* filename_string = ToCString(filename);
    int linenum = message->GetLineNumber();
    printf("%s:%i: %s\n", filename_string, linenum, exception_string);
    // Print line of source code.
    v8::String::Utf8Value sourceline(message->GetSourceLine());
    const char* sourceline_string = ToCString(sourceline);
    printf("%s\n", sourceline_string);
    // Print wavy underline (GetUnderline is deprecated).
    int start = message->GetStartColumn();
    for (int i = 0; i < start; i++) {
      printf(" ");
    }
    int end = message->GetEndColumn();
    for (int i = start; i < end; i++) {
      printf("^");
    }
    printf("\n");
  }
}

// The callback that is invoked by v8 whenever the JavaScript 'print'
// function is called.  Prints its arguments on stdout separated by
// spaces and ending with a newline.
v8::Handle<v8::Value> Print(const v8::Arguments& args) {  
  bool first = true;
  for (int i = 0; i < args.Length(); i++) {
    v8::HandleScope handle_scope;
    if (first) {
      first = false;
    } else {
      printf(" ");
    }
    v8::String::Utf8Value str(args[i]);
    const char* cstr = ToCString(str);
    printf("%s", cstr);
  }
  printf("\n");
  fflush(stdout);
  return v8::Undefined();
}

v8::Handle<v8::Value> GoogleMapsRequest(const v8::Arguments& args) {  
    v8::HandleScope handle_scope;
    v8::String::Utf8Value str(args[0]);
	string input(ToCString(str));
	
	string output = ""; 
	//Geocoder geocoder;
	if(geocoder != NULL){
		output = geocoder->SendRequest(input);	
	}

   return v8::String::New(output.c_str());
}


// Extracts a C string from a V8 Utf8Value.
const char* ToCString(const v8::String::Utf8Value& value) {
  return *value ? *value : "<string conversion failed>";
}
